package com.learning.spring.cloud.alibaba.hystrix.user.web.controller;

import com.learning.spring.cloud.alibaba.hystrix.user.web.command.HystrixCustomCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;

/**
 * ClassName: UserController
 * Description:
 * Date: 2021/7/25 23:03 【需求编号】
 *
 * @author Sam Sho
 * @version V1.0.0
 */
@Slf4j
@RestController
@RequestMapping("user")
public class UserController {


    @Autowired
    private RestTemplate restTemplate;


    /**
     * 子类实现熔断功能
     *
     * @param id
     * @return
     */
    @GetMapping("/query/{id}")
    public List<String> queryOrderByUserIdFromHystrixCommand(@PathVariable("id") Long id) {
        String serviceId = "http://01-ms-hystrix-order/order/query/user/" + id;
        return (List<String>) new HystrixCustomCommand(restTemplate, serviceId).execute();
    }

    /**
     * 添加熔断实现。测试地址：http://localhost:8001/user/query/1
     * <p>
     * 注意：熔断的状态
     * 1- 当请求失败、被拒绝、超时、断路器打开，都会进入回退。
     * 2- 但是进入回退，不一定断路器就是打开
     * 3- 断开服务 01-ms-hystrix-order ，再从访问测试地址已经进入回退逻辑
     * 4- 然后查看端点 http://localhost:8001/actuator/health 可以看到 hystrix 状态还是 up，即断路器没有打开
     * 4- 原因：进入回退，不一定在断路器就是打开，需要失败率达到一定的阈值（默认是 5s内 20次失败）
     *
     * @param id
     * @return
     */
    @GetMapping("/query1/{id}")
    @HystrixCommand(fallbackMethod = "queryOrderByUserIdFallback")
    public List<String> queryOrderByUserId(@PathVariable("id") Long id) {
        return this.restTemplate.getForObject("http://01-MS-HYSTRIX-ORDER/order/query/user/" + id, List.class);
    }


    /**
     * 熔断隔离策略与传播上下文
     * <p>
     * 1- 注解 @HystrixCommand 可以设置很多属性，如命令行参数、线程池参数
     * 2- 如果有场景需要获取回退的原因，可以在 fallbackMethod 的方法上添加 Throwable 参数
     * 3- 如果有场景某些异常不想回退，有两种方式
     * 第一, hystrix 有异常 HystrixBadRequestException，这时一个特殊异常，该异常不会回退。可以自定义异常继承 HystrixBadRequestException
     * 第二, 使用 @HystrixCommand 注解的 ignoreExceptions 属性指定不想回退的异常
     *
     * <p>
     * hystrix 线程隔离有两种：
     * 1- THREAD（线程隔离）：HystrixCommand 将在单独的线程上执行，并发请求收到线程池中的线程数量的限制。默认推荐这种。
     * 2- SEMAPHORE（信号量隔离）： HystrixCommand 将在调用线程上执行，开销较小，并发请求收到信号量个数的限制
     *
     * @param id
     * @return
     */
    @GetMapping("/query2/{id}")
    @HystrixCommand(
            groupKey = "com.learning.spring.cloud.alibaba.hystrix.user.web.controller.UserController",
            commandKey = "queryOrderByUserId",
            threadPoolKey = "HystrixThreadPool",
            fallbackMethod = "queryOrderByUserIdFallback",
            commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")},
            threadPoolProperties = {@HystrixProperty(name = "coreSize", value = "5"), @HystrixProperty(name = "maxQueueSize", value = "10")},
            ignoreExceptions = RuntimeException.class
    )
    public List<String> queryOrderByUserId2(@PathVariable("id") Long id) {
        return this.restTemplate.getForObject("http://01-MS-HYSTRIX-ORDER/order/query/user/" + id, List.class);
    }


    /**
     * 熔断回调方法：该方法与 queryOrderByUserId 方法需要有相同的参数与返回值类型
     *
     * @return
     */
    public List<String> queryOrderByUserIdFallback(Long id, Throwable throwable) {

        // 如果有场景需要获取回退的原因，可以在 fallbackMethod 的方法上添加 Throwable 参数
        log.error("访问回退：", throwable);

        List<String> list = new ArrayList<>();
        list.add("降级，返回回退的商品");
        return list;
    }


}
